-- Text
-- create by zouyb
-- 文本相关工具函数


-- 拆分字符串
local function getMsgSegments(str, c)
    local segs = {}
    local _, _, p1, p2, p3 = string.find(str, "(.-)%[(.-)%[%-](.*)");
    if(p1 == nil and p2 == nil)then
        return {{color = {r = c.r, g = c.g, b = c.b}, text = str}};
    end

    table.insert(segs, {color = {r = c.r, g = c.g, b = c.b}, text = p1});

    local _, _, color, text = string.find(p2, "(.-)](.+)");
    local r = tonumber("0x"..string.sub(color, 1, 2));
    local g = tonumber("0x"..string.sub(color, 3, 4));
    local b = tonumber("0x"..string.sub(color, 5, 6));

    table.insert(segs, {color = {r = r, g = g, b = b}, text = text});

    if p3 ~= nil then
        local p3segs = getMsgSegments(p3, c);
        segs = table.append(segs, p3segs);
    end

    return segs;
end

function testTextSegs()
    local msg = "测试[00aabb]内容[-]";
    printTable(getMsgSegments(msg), "testTextSegs");
end

-- 返回一个richText对象
function getRichText(msg, fontSize, color)
    local segs = getMsgSegments(msg, color);

    local richText = ccui.RichText:create();
    --richText:ignoreContentAdaptWithSize(false);

    local maxLength = 0;

    for i = 1, #segs do
        repeat
            local element = segs[i];
            local elementColor = element["color"];
            local text = element["text"];
            if text == "" then
                break;
            end

            local len = string.getWCharWidth(text, fontSize);
            if len > maxLength then
                maxLength = len;
            end

            local re = ccui.RichElementText:create(i,
                cc.c3b(elementColor.r, elementColor.g, elementColor.b), 255, text, getLocFontName(), fontSize);
            richText:pushBackElement(re);
        until true
    end

    richText:setContentSize(cc.size(maxLength, #segs*(fontSize+10)));
    if maxLength > 640 then
        richText:ignoreContentAdaptWithSize(true);
    end

    richText:setLocalZOrder(10);
    return richText;
end

-------------------------------
-- 返回一个单行文本， 自己用label拼接
-- @param msg         文本的内容
-- @param style       字体风格
-- @param alignCenter 是否居中显示
-- @return 包含一个单行文本的Node
function getTextLine(msg, style, alignCenter)
    local fontSize = style["fontSize"];
    local color = cc.c3b(style.color.r, style.color.g, style.color.b);
    local outlineColor = style["outlineColor"];
    local outlineSize = style["outlineSize"];

    local segs = getMsgSegments(msg, color);

    local node = cc.Node:create();
    node:setAnchorPoint(cc.p(0, 0));

    local totalLen = 0;
    local lineHeight = fontSize + 5;
    for i = 1, #segs do
        repeat
            local element = segs[i];
            local elementColor = element["color"];
            local text = element["text"];
            if text == "" then
                break;
            end

            local len = string.getWCharWidth(text, fontSize);

            local label = ccui.Text:create();
            label:setAnchorPoint(cc.p(0, 0));
            label:setString(text);
            label:setFontSize(fontSize);
            label:setFontName(getLocFontName());
            label:setTextColor(cc.c4b(elementColor.r, elementColor.g, elementColor.b, 255));
            if outlineColor ~= nil then
                label:enableOutline(cc.c4b(outlineColor[1], outlineColor[2], outlineColor[3], 255), outlineSize);
            end

            label:setPositionX(totalLen);
            label:setContentSize(len, lineHeight);
            node:addChild(label);

            totalLen = totalLen + len;
        until true;
    end

    -- 如果需要居中显示，则调整一下位置。
    if true == alignCenter then
        node:setPosition(-totalLen/2, -lineHeight/2);
    end

    return node;
end

-------------------------------------------------------------------------------
-- 生成艺术字并旋转
-- @function genArtWordAndRotate
-- @param textNode 目标文本节点
-- @param fontSize 字体大小
-- @param bgPath 底部纹理图片
function genArtWordAndRotate(textNode, fontSize, bgPath)
    resetEffectText(textNode);
    local str = textNode:getString();
    local len = string.getWCharCount(str);
    for i = 1, len do
        local effectNodeList = addEffectText(textNode, 0, 0, str, i, fontSize, bgPath);
        local function doEffect(node)
            node:setVisible(false);
            node:setScale(1.3);
            local duration = 2.5 / len;
            local callFunc1 = cc.CallFunc:create(function()
                node:setVisible(true);
                local rotate = cc.RotateBy:create(duration, 360);
                local fadeIn = cc.FadeIn:create(duration);
                local scaleBy = cc.ScaleTo:create(duration, 1);
                node:runAction(rotate);
                node:runAction(fadeIn);
                node:runAction(scaleBy);
            end);

            local delay = cc.DelayTime:create(0.5 / len *i);
            node:runAction(cc.Sequence:create(delay, callFunc1));
        end

        for _, effectNode in pairs(effectNodeList) do
            doEffect(effectNode);
        end
    end
end

-------------------------------------------------------------------------------
-- 生成艺术字并旋转飞入
-- @function genArtWordAndRotateFlyIn
-- @param textNode 目标文本节点
-- @param fontSize 字体大小
-- @param bgPath 底部纹理图片
function genArtWordAndRotateFlyIn(textNode, fontSize, bgPath)
    resetEffectText(textNode);
    local str = textNode:getString();
    local len = string.getWCharCount(str);
    for i = 1, len do
        local effectNodeList = addEffectText(textNode, 0, 0, str, i, fontSize, bgPath);
        local function doEffect(tmpNode)
            tmpNode:setVisible(false);
            local callFunc = cc.CallFunc:create(function()
                    tmpNode:setVisible(true);
                    tmpNode:setScale(1.5);
                    tmpNode:setOpacity(0);
                    tmpNode:setPositionX(tmpNode:getPositionX() - 200);
                end);

            local duration = 0.2;
            local delay = cc.DelayTime:create(duration / 2 * i);
            local fadeIn = cc.FadeIn:create(duration);
            local rotate = cc.RotateBy:create(duration, 360);
            local scaleTo = cc.ScaleTo:create(duration, 1);
            local moveBy = cc.MoveBy:create(duration, cc.p(200, 0));

            tmpNode:runAction(cc.Sequence:create(delay, callFunc, cc.Spawn:create(fadeIn, rotate, scaleTo, moveBy)));
            AudioM.playFx("new_level");
        end

        for _, effectNode in pairs(effectNodeList) do
            doEffect(effectNode);
        end
    end
end

-------------------------------------------------------------------------------
-- 添加特效文本
-- example: addEffectText(self, 300, 400, "大", 30, "images/hero/1.png");
-- @function addEffectText
-- @param parent 父节点
-- @param x X坐标
-- @param y Y坐标
-- @param wholeText 待显示文本
-- @param index 第几个字
-- @param fontSize 字体大小
-- @param bg 底图文件名
function addEffectText(parent, x, y, wholeText, index, fontSize, bg)
    local olTextColor = outlineTextColor or TextStyleM.TEXT_OUTLINE_COLOR_DARK;

    if parent.sampleTextNode == nil then
        -- 创建一个文本节点，用来索引字符位置
        parent.sampleTextNode = cc.Label:createWithTTF(wholeText, getLocFontName(), fontSize);
        parent.sampleTextNode:setPositionX(-DESIGN_WIDTH*100);
        parent:addChild(parent.sampleTextNode);

        -- 左面叠一层
        parent.bottomLabelLeft = cc.Label:createWithTTF(wholeText, getLocFontName(), fontSize);
        local size = parent.bottomLabelLeft:getContentSize();
        parent.bottomLabelLeft:setPosition(size.width / 2 - 1, size.height / 2);
        parent.bottomLabelLeft:setTextColor(TextStyleM.TEXT_COLOR_DARK_BROWN);
        parent.bottomLabelLeft:setOpacity(100);
        parent:addChild(parent.bottomLabelLeft);

        parent.bottomLabelLeft2 = cc.Label:createWithTTF(wholeText, getLocFontName(), fontSize);
        parent.bottomLabelLeft2:setPosition(size.width / 2 - 2, size.height / 2);
        parent.bottomLabelLeft2:setTextColor(TextStyleM.TEXT_COLOR_DARK_BROWN);
        parent.bottomLabelLeft2:setOpacity(10);
        parent:addChild(parent.bottomLabelLeft2);

        -- 上面叠一层
        parent.bottomLabelTop = cc.Label:createWithTTF(wholeText, getLocFontName(), fontSize);
        parent.bottomLabelTop:setPosition(size.width / 2, size.height / 2 + 1);
        parent.bottomLabelTop:setTextColor(TextStyleM.TEXT_COLOR_DARK_BROWN);
        parent.bottomLabelTop:setOpacity(100);
        parent:addChild(parent.bottomLabelTop);

        parent.bottomLabelTop2 = cc.Label:createWithTTF(wholeText, getLocFontName(), fontSize);
        parent.bottomLabelTop2:setPosition(size.width / 2, size.height / 2 + 2);
        parent.bottomLabelTop2:setTextColor(TextStyleM.TEXT_COLOR_DARK_BROWN);
        parent.bottomLabelTop2:setOpacity(10);
        parent:addChild(parent.bottomLabelTop2);

        -- 下面叠三层
        parent.bottomLabel1 = cc.Label:createWithTTF(wholeText, getLocFontName(), fontSize);
        parent.bottomLabel1:setPosition(size.width / 2 + 1, size.height / 2 - 1);
        parent.bottomLabel1:setTextColor(TextStyleM.TEXT_COLOR_BLACK);
        parent:addChild(parent.bottomLabel1);

        parent.bottomLabel2 = cc.Label:createWithTTF(wholeText, getLocFontName(), fontSize);
        parent.bottomLabel2:setPosition(size.width / 2 + 2, size.height / 2 - 2);
        parent.bottomLabel2:setTextColor(TextStyleM.TEXT_COLOR_BLACK);
        parent:addChild(parent.bottomLabel2);

        parent.bottomLabel3 = cc.Label:createWithTTF(wholeText, getLocFontName(), fontSize);
        parent.bottomLabel3:setPosition(size.width / 2 + 3, size.height / 2 - 3);
        parent.bottomLabel3:setTextColor(TextStyleM.TEXT_COLOR_BLACK);
        parent:addChild(parent.bottomLabel3);
    end

    local tmpSp = parent.sampleTextNode:getLetter(index-1);
    local str = (string.getWChar(wholeText, index));

    offset = offset or cc.p(0, 0);

    local effectNode = cc.Node:create();
    if tmpSp ~= nil then
        effectNode:setPosition(tmpSp:getPositionX(), tmpSp:getPositionY());
    end
    parent:addChild(effectNode);

    -- 如果是空格/全角空格/tab 就直接返回textNode
    if str == " " then
        effectNode:setPosition(0, fontSize / 4);
        return {};
    elseif str == "　" or str == "\t" then
        effectNode:setPosition(0, fontSize / 2);
        return {};
    end

    parent:setFontName(getLocFontName());
    parent:setFontSize(fontSize);
    parent:setTextColor(olTextColor);
    parent:enableShadow(TextStyleM.TEXT_OUTLINE_COLOR_DARK, cc.size(4, -4));

    -- -- 模板文本
    local sampleText = cc.Label:createWithTTF(str, getLocFontName(), fontSize);
    sampleText:setPositionX(-DESIGN_WIDTH*100);
    effectNode:addChild(sampleText);

    local sprite = sampleText:getLetter(0);
    local mask = cc.Sprite:createWithSpriteFrame(sprite:getSpriteFrame());

    local texture = cc.Sprite:create(bg);
    local target = cc.RenderTexture:create(mask:getContentSize().width, mask:getContentSize().height);

    mask:setBlendFunc(gl.ONE, gl.ZERO);
    mask:setPosition(mask:getContentSize().width/2, mask:getContentSize().height/2);
    texture:setBlendFunc(gl.DST_ALPHA, gl.ZERO);
    texture:setPosition(mask:getContentSize().width/2, mask:getContentSize().height/2);
    texture:setScale(fontSize / texture:getContentSize().width);

    target:beginWithClear(0, 0, 0, 255);
    mask:visit();
    texture:visit();
    target:endToLua();

    target:getSprite():getTexture():setAntiAliasTexParameters();
    effectNode:addChild(target);


    local effectNode2 = parent:getVirtualRenderer():getLetter(index - 1);
    local effectNode3 = parent.bottomLabel1:getLetter(index - 1);
    local effectNode4 = parent.bottomLabel2:getLetter(index - 1);
    local effectNode5 = parent.bottomLabel3:getLetter(index - 1);
    local effectNodeLeft = parent.bottomLabelLeft:getLetter(index - 1);
    local effectNodeTop = parent.bottomLabelTop:getLetter(index - 1);

    return { effectNode, effectNode2, effectNode3, effectNode4, effectNode5, effectNodeLeft, effectNodeTop };
end

---------------------------------------------------------------------------
-- 重置特效文本 (做一些清理工作)
function resetEffectText(parent)
    if parent.sampleTextNode then
        parent:removeChild(parent.sampleTextNode);
        parent.sampleTextNode = nil;
    end

    parent:removeChild(parent.bottomLabelLeft);
    parent:removeChild(parent.bottomLabelLeft2);
    parent:removeChild(parent.bottomLabelTop);
    parent:removeChild(parent.bottomLabelTop2);
    parent:removeChild(parent.bottomLabel1);
    parent:removeChild(parent.bottomLabel2);
    parent:removeChild(parent.bottomLabel3);
end

-- 英文版的部分字母底图偏移有问题，需要修正
function getOlOffest(str, fontSize)
    -- 没想到好方法...只能按字符逐个调整了
    if getLang() ~= "en" then
        return 0;
    end

    local needOlOffset1 = { "n", "t", "h", };
    local needOlOffset2 = { "g", "j", "p", "q", "y", };
    local needOlOffset3 = { "'" };
    local olOffset = -3;

    if table.indexOf(needOlOffset1, str) ~= -1 then
        olOffset = -3 + fontSize / 20;
    end

    if table.indexOf(needOlOffset2, str) ~= -1 then
        olOffset = 3 + fontSize / 20;
    end

    if table.indexOf(needOlOffset3, str) ~= -1 then
        olOffset = 2 - fontSize / 2;
    end

    return olOffset;
end

-- 拆分字符串
local function parseText(str)
    local segs = {}
    local _, _, p1, p2, p3 = string.find(str, "(.-)%[(.-)%[%-](.*)");
    if(p1 == nil and p2 == nil)then
        return {{color = TextStyleM.TEXT_COLOR_WHITE, text = str}};
    end

    table.insert(segs, {color = TextStyleM.TEXT_COLOR_WHITE, text = p1});

    local _, _, color, text = string.find(p2, "(.-)](.+)");
    local textColor = TextStyleM.getColor(color);

    if color == "url" then
        -- 超链接
        textColor = TextStyleM.getColor("green");
        local list = splitString(text, "\n");
        if #list == 2 then
            table.insert(segs, {color = textColor, text = list[2], url = list[1]});
        else
            cclog("URL类文字格式非法！不予显示");
        end
    elseif color and string.sub(color, 1, 5) == "sFont" then
        -- 系统字
        if string.len(color) > 7 then
            -- 有设置颜色
            textColor = TextStyleM.getColor(string.sub(color, 7));
        end
        local text1 = ccui.Text:create();
        text1:setString(text);
        local count = text1:getContentSize().width / 3 + 2;

        local changeText = string.rep(" ", count);
        table.insert(segs, {color = textColor, text = changeText, sFont = text});
    elseif color and string.sub(color, 1, 4) == "goto" then
        -- 界面跳转
        local _, _, gotoFlag, uiIndex, changeColor = string.find(color, "(%a+),(%d+),(%a+)");
        if changeColor ~= "nil" then
            textColor = TextStyleM.getColor(changeColor);
        end
        table.insert(segs, {color = textColor, text = text, uiIndex = uiIndex});
    elseif color and string.sub(color, 1, 5) == "image" then
        -- 插入图片
        local arr = string.explode(string.sub(color, 1, -1), ",")
        local changeText = " ";
        for i = 1, tonumber(arr[2]) do
            changeText = changeText .. "\n";
        end
        table.insert(segs, {color = textColor, text = changeText,
            image = {text, tonumber(arr[3]), tonumber(arr[4])}});
    else
        -- 颜色文字
        table.insert(segs, {color = textColor, text = text});
    end

    if p3 ~= nil then
        local p3segs = parseText(p3);
        segs = table.append(segs, p3segs);
    end

    return segs;
end

-----------------------------------
-- 抽取文本
-- @function [parent=TextStyleM]
-- @param text 待显示文本 范例： 我爱[red]祖国[-]天安门
-- @param kerning 文字间距
function extractString(text, kerning)
    local segs = parseText(text);

    local finalText = "";
    local temp = {};
    for _, seg in pairs(segs) do
        table.insert(temp, seg.text);
    end
    finalText = table.concat(temp, "");

    return finalText;
end

--------------------------------------
-- 设置多彩文本
-- @function [#parent=TextStyleM]
-- @param textNode 目标文本节点, 需要为cc.Text类型
-- @param text 待显示文本 范例： 我爱[red]祖国[-]天安门
-- @param kerning 文字间距
-- @param lineHight 行距
-- @param wordCount 每行文字数量
function applyString(textNode, text, kerning, noLocFix, lineHeight, wordCount)
    textNode = replaceTextNode(textNode);

    local textNode = applyString2(textNode, text, kerning, noLocFix, lineHeight, wordCount);

    return textNode;
end

--------------------------------------
-- 设置多彩文本
-- @function [#parent=TextStyleM]
-- @param textNode 目标文本节点, 需要为cc.Text类型
-- @param text 待显示文本 范例： 我爱[red]祖国[-]天安门
-- @param kerning 文字间距
-- @param lineHight 行距
-- @param wordCount 每行文字数量
function applyString2(textNode, text, kerning, noLocFix, lineHeight, wordCount)
    local segs = parseText(text);

    local finalText = "";
    local temp = {};
    for _, seg in pairs(segs) do
        table.insert(temp, seg.text);
    end
    finalText = table.concat(temp, "");

    if kerning ~= nil then
        textNode:getVirtualRenderer():setAdditionalKerning(getLocKerning() -4);
    end

    -- 设置自动换行
    local fontSize = textNode:getFontSize();

    if lineHeight ~= nil then
        textNode:getVirtualRenderer():setLineHeight(lineHeight);
    else
        textNode:getVirtualRenderer():setLineHeight(fontSize);
    end

    -- 如果是英文版本，字号已经是缩过的了，需要放回去
    -- 临时处理，之后要修改
    local oldFontSize = fontSize;
    oldFontSize = fontSize / getLocFontSizeMulti(noLocFix);
    wordCount = wordCount or TextStyleM.TEXT_WORD_COUNT_PER_LINE;

    -- 最大行宽使用文本控件上已经设置好的格式，不再重新设置
    -- textNode:getVirtualRenderer():setMaxLineWidth(wordCount * oldFontSize);
    textNode:setString(finalText);

    local oldColor = textNode:getTextColor();

    local function delay()
        textNode:setTextColor(TextStyleM.TEXT_COLOR_WHITE);

        local count = 0;
        local start = 0;
        local paddingCount = 0;

        for _, seg in pairs(segs) do
            start = count;
            count = count + string.getWCharCount(seg.text);

            local finalColor = oldColor;
            if not TextStyleM.isWhite(seg.color)  then
                finalColor = seg.color;
            end

            local i = start + paddingCount;
            local j = 0;
            local k = 0;
            local endPos = count - 1 + paddingCount;
            local charPos = 0;
            local curPaddingCount = 0;
            local firstLetter = true;
            local posY;
            local posX1;
            local posX2;
            local letterHeight;
            local letterWidth;
            while i <= endPos do
                local letter = textNode:getVirtualRenderer():getLetter(i);
                local nextLetter = textNode:getVirtualRenderer():getLetter(i+1);
                if letter then
                    if firstLetter then
                        firstLetter = false;
                        posY = letter:getPositionY() - 5;
                        posX1 = letter:getPositionX();
                        local letterSize = getRealContentSize(letter);
                        letterHeight = letterSize.height;
                        letterWidth = letterSize.width;
                    end
                    if i == endPos then
                        posX2 = letter:getPositionX();
                    end
                    textNode:getVirtualRenderer():setLetterColor(i, finalColor);
                else
                    if nextLetter ~= nil and not string.isBreakLineChar(seg.text, charPos-curPaddingCount)
                       and string.getWChar(seg.text, charPos-curPaddingCount+1) ~= " " then
                        if isFullWidthLang()
                           or getLang() == "en" and j % (k * 2) == 0 then
                            endPos = endPos + 1;
                            paddingCount = paddingCount + 1;
                            curPaddingCount = curPaddingCount + 1;
                            j = 0;
                        end
                        j = j + 1;
                        k = k + 1;
                    end
                end

                i = i + 1;
                charPos = charPos + 1;
            end

            if seg.url then
                -- 文本超链接处理
                local urlAddress = seg.url;
                -- 手动拼上HTTP TODO 暂时这么处理
                if not string.find(urlAddress, "http") then
                    urlAddress = "http://" .. urlAddress;
                end
                local function onClicked(sender, eventType)
                    if eventType == ccui.TouchEventType.ended then
                        -- 打开超链接
                        -- 考虑到重大公告没有加载这个模块
                        if AudioM then
                            AudioM.playFx("button_click");
                        end
                        cc.Application:getInstance():openURL(urlAddress);
                    end
                end
                -- 使用透明的图片来接收点击事件
                local icon;
                if not _G["gameScene"] then
                    icon = ccui.ImageView:create(getSettingIconPath("system", "default"));
                else
                    icon = ccui.ImageView:create("images/ui/setting/default.png");
                end
                icon:setOpacity(0);
                icon:setTouchEnabled(true);
                icon:addTouchEventListener(onClicked);
                icon:setPosition(cc.p(posX1 - 10, posY + 5));
                icon:setAnchorPoint(0, 0.5);
                -- 缩小到对应字范围
                local imageSize = getRealContentSize(icon);
                icon:setScaleX((posX2 - posX1 + 10)/ imageSize.width * 1.1);
                icon:setScaleY(letterHeight / imageSize.height * 2.0);
                textNode:addChild(icon);

                -- 下划线
                local drawNode = cc.DrawNode:create();
                drawNode:setVisible(true);
                textNode:addChild(drawNode);
                local color4F = TextStyleM.changeColor4BTo4F(finalColor);
                drawNode.drawSegment(drawNode, cc.p(posX2 + letterWidth / 2, posY - letterHeight / 3),
                   cc.p(posX1 - letterWidth / 2, posY - letterHeight / 3), 1.0, color4F);
            elseif seg.sFont then
                -- 系统字处理
                -- 替换的文字
                local text = ccui.Text:create();
                text:setString(seg.sFont);
                -- 用系统字/直接用原大小无需修正
                text:setFontSize(fontSize);
                text:setTextColor(finalColor);
                text:setAnchorPoint(0.5, 1);
                text:setPosition((posX2 - posX1) / 2 + posX1, posY + 5);
                textNode:addChild(text);
            elseif seg.uiIndex then
                -- 页面跳转处理
                local function onClicked(sender, eventType)
                    if eventType == ccui.TouchEventType.ended then
                        -- 放到函数里去做跳转事件
                        FormulaM.invoke("LINK_TO_UI", seg.uiIndex);
                    end
                end
                -- 使用透明的图片来接收点击事件
                local icon = ccui.ImageView:create(getSettingIconPath("system", "02"));
                icon:setOpacity(0);
                icon:setTouchEnabled(true);
                icon:addTouchEventListener(onClicked);
                icon:setPosition(cc.p(posX1 - 10, posY + 5));
                icon:setAnchorPoint(0, 0.5);
                -- 缩小到对应字范围
                local imageSize = getRealContentSize(icon);
                icon:setScaleX((posX2 - posX1 + 10)/ imageSize.width * 1.1);
                icon:setScaleY(letterHeight / imageSize.height * 2.0);
                textNode:addChild(icon);
                -- 下划线
                local drawNode = cc.DrawNode:create();
                drawNode:setVisible(true);
                textNode:addChild(drawNode);
                local color4F = TextStyleM.changeColor4BTo4F(finalColor);
                drawNode.drawSegment(drawNode, cc.p(posX2 + letterWidth / 2, posY - letterHeight / 3),
                   cc.p(posX1 - letterWidth / 2, posY - letterHeight / 3), 1.0, color4F);
            elseif seg.image then
                -- 插入图片
                local image = ccui.ImageView:create(seg.image[1]);
                image:setPosition(cc.p(posX1 + seg.image[2], posY + seg.image[3]));
                textNode:addChild(image);
            end
        end
    end

    textNode:visit();
    delay();

    return textNode;
end


local jumpStop = {};

-- 跳动状态设置
function setJumpStop(flag, state)
    jumpStop[flag] = state;
end

-- 处理文字跳动
function setTextJump(count, TextNode, mainNode, flag, delayTime)
    if jumpStop[flag] == false then
        -- 相关文字已经在跳动了,不处理
        return;
    else
        jumpStop[flag] = false;
    end

    local i = 0;
    local letterNode = {};
    local jumpFlag = 0;

    while i < count do
        letterNode[i] = TextNode:getVirtualRenderer():getLetter(i);
        i = i + 1;
    end

    if not delayTime then
        delayTime = 0.3;
    end

    -- 处理文字跳动相关函数
    local function dealAction(count, letterNode, mainNode, flag, delayTime)
        local jumpFlag = 0;

        local function jump()
            local a = letterNode;
            if jumpFlag == count then
                jumpFlag = 0;
            else
                local jumpBy = cc.JumpBy:create(delayTime * 1, cc.p(0, 0), 5, 1);
                letterNode[jumpFlag]:runAction(jumpBy);

                jumpFlag = jumpFlag + 1;
            end

            if jumpStop[flag] ~= true then
                performWithDelay(mainNode, jump, delayTime);
            end
        end

        jump();
    end

    dealAction(count, letterNode, mainNode, flag, delayTime);
end

-- 处理艺术字跳动
function setArtWordJump(count, TextNode, mainNode, flag, delayTime)
    if jumpStop[flag] == false then
        -- 相关文字已经在跳动了,不处理
        return;
    else
        jumpStop[flag] = false;
    end

    local i = 0;
    local letterNode = {};
    local jumpFlag = 0;

    for i = 1, count do
        local childNode = TextNode:getChildByName(TextNode:getName() .. "artWord" .. i);
        if childNode ~= nil then
            letterNode[i - 1] = childNode;
        end
    end

    if #letterNode == 0 then
        -- 没有节点要处理
        return;
    end

    if not delayTime then
        delayTime = 0.3;
    end

    -- 处理文字跳动相关函数
    local function dealAction(count, letterNode, mainNode, flag, delayTime)
        local jumpFlag = 0;

        local function jump()
            local a = letterNode;
            if jumpFlag == count then
                jumpFlag = 0;
            else
                local jumpBy = cc.JumpBy:create(delayTime * 1, cc.p(0, 0), 5, 1);
                letterNode[jumpFlag]:runAction(jumpBy);

                jumpFlag = jumpFlag + 1;
            end

            if jumpStop[flag] ~= true then
                performWithDelay(mainNode, jump, delayTime);
            end
        end

        jump();
    end

    dealAction(count, letterNode, mainNode, flag, delayTime);
end

-- 处理文字旋转特效， 获得新的冈布奥时用到
function setTextRotate(count, TextNode, mainNode, degree, accFactor)
    if mainNode.rotateStopFlag == false then
        -- 相关文字已经在旋转
        return;
    else
        mainNode.rotateStopFlag = false;
    end

    if degree == nil then
        degree = 360;
    end

    local i = 0;
    local letterNode = {};
    local jumpFlag = 0;

    while i < count do
        letterNode[i] = TextNode:getVirtualRenderer():getLetter(i);
        -- 透明处理
        letterNode[i]:setOpacity(0);
        i = i + 1;
    end

    -- 计算旋转时间间隔
    accFactor = accFactor or 1.0;
    local interval = 0.2 / accFactor;

    -- 处理文字旋转相关函数
    local function dealAction(count, letterNode, mainNode)
        local rotateFlag = 0;

        local function rotate()
            local a = letterNode;
            if rotateFlag == count then
                mainNode.rotateStopFlag = true;
                return;
            elseif letterNode[rotateFlag] then
                letterNode[rotateFlag]:setScale(2.0)

                -- 计算单个字旋转的时间
                local duration = 0.5 / accFactor;
                local rotateBy = cc.RotateBy:create(duration, degree);
                local fadeInBy = cc.FadeIn:create(duration);
                local scaleBy = cc.ScaleBy:create(duration, 0.5, 0.5);

                -- TODO 该断代码存在报错，暂时先加保护
                if scaleBy and fadeInBy and rotateBy then
                    letterNode[rotateFlag]:runAction(scaleBy);
                    letterNode[rotateFlag]:runAction(fadeInBy);
                    letterNode[rotateFlag]:runAction(rotateBy);
                end

                rotateFlag = rotateFlag + 1;
            end

            if mainNode.rotateStopFlag ~= true then
                performWithDelay(mainNode, rotate, interval);
            end
        end

        rotate();
    end

    dealAction(count, letterNode, mainNode);
end

-- 处理文字飞入特效， 获得新的称号时用到
function textFlyInEffect(count,TextNode, mainNode)
    if mainNode.flyFlag == false then
        return;
    else
        mainNode.flyFlag = false;
    end

    local i = 0;
    local letterNode = {};
    local jumpFlag = 0;

    while i < count do
        letterNode[i] = TextNode:getVirtualRenderer():getLetter(i);
        -- 透明处理
        letterNode[i]:setOpacity(0);
        i = i + 1;
    end

    -- 处理文字旋转相关函数
    local function dealAction(count, letterNode, mainNode)
        local flag = 0;

        local function flyIn()
            local a = letterNode;
            if flag == count then
                mainNode.flyFlag = true;
                return;
            else
                letterNode[flag]:setScale(2.0)
                local fadeInBy = cc.FadeIn:create(0.5);
                local scaleBy = cc.ScaleBy:create(0.5, 0.5, 0.5);

                letterNode[flag]:runAction(scaleBy);
                letterNode[flag]:runAction(fadeInBy);

                flag = flag + 1;
            end

            if mainNode.flyFlag ~= true then
                performWithDelay(mainNode, flyIn, 0.3);
            end
        end

        flyIn();
    end

    dealAction(count, letterNode, mainNode);
end

-- 数字滚动(递增)
function scoreScrolling(textNode, val, callback, speed)
    if val <= 1 then
        if callback ~= nil then
            callback();
        end
        return;
    end

    local startVal = val - 100;
    startVal = iif(startVal <= 0, 1, startVal);
    local function scrolling()
        textNode:setString(tostring(startVal))
        if startVal == val then
            if callback ~= nil then
                callback();
            end
            return;
        end

        if val - startVal > 51 then
            startVal = startVal + 51;
        elseif val - startVal > 21 then
            startVal = startVal + 21;
        elseif val - startVal > 11 then
            startVal = startVal + 11;
        else
            startVal = startVal + 1;
        end

        -- 播放滚动音效
--        AudioM.playFx("stat_scrolling");

        performWithDelay(textNode, scrolling, 1 / speed);
    end
    scrolling();
end

-- 数字滚动(递减)
function scoreScrolling2(textNode, val, callback, speed)
    local startVal = tonumber(textNode:getString());
    if startVal <= val then
        if callback ~= nil then
            callback();
        end
        return;
    end

    local function scrolling()
        textNode:setString(tostring(startVal))
        if startVal <= val then
            if callback ~= nil then
                callback();
            end
            return;
        end

        if startVal - val > 51 then
            startVal = startVal - 51;
        elseif startVal - val > 21 then
            startVal = startVal - 21;
        elseif startVal - val > 11 then
            startVal = startVal - 11;
        else
            startVal = startVal - 1;
        end

        -- 播放滚动音效
--        AudioM.playFx("stat_scrolling");

        performWithDelay(textNode, scrolling, 1 / speed);
    end
    scrolling();
end

-- 当前数字滚动到增加值
-- 数字滚动(递增)
function gemUpScrolling(textNode, val, startVal, callback, speed)
    local val = tonumber(val);
    local increaseVal = val - startVal;

    if increaseVal < 1 then
        -- 可能是钻石减少,直接设置钻石数
        textNode:setString(val);

        if callback ~= nil then
            callback();
        end
        return;
    end

    startVal = iif(startVal <= 0, 0, startVal);
    local function scrolling()
        textNode:setString(tostring(startVal))
        if startVal == val then
            if callback ~= nil then
                callback();
            end
            return;
        end

        if val - startVal > 51 then
            startVal = startVal + 51;
        elseif val - startVal > 21 then
            startVal = startVal + 21;
        elseif val - startVal > 11 then
            startVal = startVal + 11;
        else
            startVal = startVal + 1;
        end

        -- 播放滚动音效
        --        AudioM.playFx("stat_scrolling");

        performWithDelay(textNode, scrolling, 1 / speed);
    end
    scrolling();
end

--------------------------------
--@param preText    上一个处理过的文本（必须是设置过文本风格的）
--@param spacing    两段文本的间距（默认5像素）
function setNextTextPosY(preText, nextText, spacing)
    -- 两段文本必须是设置过文本风格的
    -- 获取上一个文本的纵坐标、高度、锚点位置
    local prePosY    = preText:getPositionY();
    local preHeight  = preText:getVirtualRenderer():getContentSize().height;
    local preAnchorY = preText:getAnchorPoint().y;

    -- 获取下一个文本的高度、锚点位置
    local nextHeight  = nextText:getVirtualRenderer():getContentSize().height;
    local nextAnchorY = nextText:getAnchorPoint().y;

    local nextPosY;
    local spacValue = spacing or 5;

    nextPosY = prePosY - preHeight * preAnchorY -
               spacValue -
               nextHeight * (1 - nextAnchorY);

    nextText:setPosition(nextText:getPositionX(), nextPosY);
    return nextPosY;
end

function setNextTextPosX(preText, nextText, spacing)
    -- 两段文本必须是设置过文本风格的
    -- 获取上一个文本的横坐标、高度、锚点位置
    local prePosX    = preText:getPositionX();
    local preWidth  = preText:getVirtualRenderer():getContentSize().width;
    local preAnchorX = preText:getAnchorPoint().x;

    -- 获取下一个文本的高度、锚点位置
    local nextWidth  = nextText:getVirtualRenderer():getContentSize().width;
    local nextAnchorX = nextText:getAnchorPoint().x;

    local nextPosX;
    local spacValue = spacing or 5;

    nextPosX = prePosX - preWidth * preAnchorX -
               spacValue -
               nextWidth * (1 - nextAnchorX);

    nextText:setPosition(nextPosX, nextText:getPositionY());
    return nextPosX;
end

function setNextTextPosRightX(preText, nextText, spacing)
    -- 两段文本必须是设置过文本风格的
    -- 获取上一个文本的横坐标、高度、锚点位置
    local prePosX    = preText:getPositionX();
    local preWidth  = preText:getVirtualRenderer():getContentSize().width;
    local preAnchorX = preText:getAnchorPoint().x;

    -- 获取下一个文本的高度、锚点位置
    local nextWidth  = nextText:getVirtualRenderer():getContentSize().width;
    local nextAnchorX = nextText:getAnchorPoint().x;

    local nextPosX;
    local spacValue = spacing or 5;

    nextPosX = prePosX + preWidth * preAnchorX +
               spacValue +
               nextWidth * (1 - nextAnchorX);

    nextText:setPosition(nextPosX, nextText:getPositionY());
    return nextPosX;
end

function setNextNodePosX(preText, nextNode, spacing)
    -- 获取上一个文本的横坐标、高度、锚点位置
    local prePosX    = preText:getPositionX();
    local preWidth  = preText:getVirtualRenderer():getContentSize().width;
    local preAnchorX = preText:getAnchorPoint().x;

    -- 获取下一个文本的高度、锚点位置
    local nextWidth  = nextNode:getContentSize().width;
    local nextAnchorX = nextNode:getAnchorPoint().x;

    local nextPosX;
    local spacValue = spacing or 5;

    nextPosX = prePosX - preWidth * preAnchorX -
               spacValue -
               nextWidth * (1 - nextAnchorX);

    nextNode:setPosition(nextPosX, nextNode:getPositionY());
    return nextPosX;
end

------------------------
--@param textNode           待处理的文本节点
--@param useBg              使用的衬底
--@param outlineTextColor   衬底文本颜色
--@param outlineColor       衬底描边颜色
--@param offset             底下文字和美术字的偏移
--@param shadowOffset       底下文字的阴影偏移
function convertTextToArtWord(textNode, useBg, outlineTextColor, outlineColor, offset, shadowOffset, totalOffset)
    -- 暂时禁止使用艺术字
    if true then
        local kerning = -4;
        if not isChineseLang() then
            if getLang() == "th" then
                kerning = 0;
            else
                kerning = -3;
            end
        end
        textNode:setColor(TextStyleM.TEXT_COLOR_WHITE);
        textNode:getVirtualRenderer():setAdditionalKerning(kerning);
        if useBg == "images/ui/text_mask/text_mask2.png" then
            textNode:setTextColor(TextStyleM.TEXT_COLOR_LIGHT_ORANGE);
        else
            textNode:setTextColor(TextStyleM.TEXT_COLOR_ORANGE);
        end

        return;
    end

    -- 未传入衬底，则使用默认的
    local textBg = useBg or "images/ui/text_mask/text_mask.png";

    -- 使用文本节点的字号
    local textFontSize = textNode:getFontSize();

    -- 未传入颜色，使用默认颜色
    local olTextColor = outlineTextColor or TextStyleM.TEXT_COLOR_DARK_BLUE;
    local olColor = outlineColor or TextStyleM.TEXT_COLOR_OUTLINE;

    -- 描边信息
    local olSize = textNode:getOutlineSize();

    -- 获取文本信息
    local str = textNode:getString();

    local len = string.getWCharCount(str);

    local kerning = textNode:getVirtualRenderer():getAdditionalKerning();

    -- 先移除曾经添加过的艺术字节点
    textNode:removeAllChildren();

    -- 重置文本节点
    textNode:setString("");

    -- 原来的字体宽度
    local oldContentWidth = textNode:getContentSize().width;

    -- 特殊字符  需要增加横向的偏移
    -- 英文版中部分字母 会出现向左的偏移，这里给掰回去...
    local posXOffset = 0;
    local posX = 0;
    local effectStrTotalWidth = 0;
    local firstWidth = nil;
--    local nodeOffset = totalOffset or cc.p(0, 0);
--    posX = posX + nodeOffset.x;
--    local posY = 0;
--    posY = posY + nodeOffset.y;

    local nodeList = {};
    for i = 1, len do
        local char = (string.getWChar(str, i));

        local effectNode, width = addEffectText(textNode, 0, 0, char, textFontSize, textBg, olTextColor, olColor, offset, shadowOffset);
        effectNode:setName(textNode:getName() .. "artWord" .. i);

        -- if i > 1 then
        --     posX = posX + width + / 2 + oldWidth / 2 + textFontSize  / 10;
        -- end
        local offsetXFix  = 0;
        local offsetXFix2 = 0;
        if table.indexOf({ "s", }, char) ~= -1 then
            offsetXFix = textFontSize / 6;
        elseif table.indexOf({ "t", "y", "v", }, char) ~= -1 then
            offsetXFix = textFontSize / 8;
        elseif table.indexOf({ "e", "c", }, char) ~= -1 then
            offsetXFix = textFontSize / 10;
        elseif table.indexOf({ "1", }, char) ~= -1 then
            offsetXFix2 = textFontSize / 6;
        end

        effectNode:setPositionX(posX + offsetXFix + effectNode:getPositionX());

        if firstWidth == nil then
            firstWidth = width;
        end

        -- 特效文本的总宽度
        effectStrTotalWidth = posX + offsetXFix + width / 2 + firstWidth / 2;

        -- 下一个文字的中心位置
        posX = posX + offsetXFix + width + textFontSize / 20 + offsetXFix2;

        effectNode:setVisible(true);

        table.insert(nodeList, effectNode);
    end

    -- 计算偏差
    local diff = (oldContentWidth - effectStrTotalWidth) / 2;

    -- 由于effectNode是作为textNode的子节点添加， 默认加在左下角， 此处统一再做一次偏移，只得整体居中
    for _, node in pairs(nodeList) do
        node:setPositionX(node:getPositionX() + diff);
    end
end

----------------------
--@param textNode   文本节点
--@param text       替换的文字
--@param iconPath   用于替换的资源
--@param scale      缩放比例
function replaceTextWithRes(textNode, text, iconPath, scale)
    local str = textNode:getString();
    local len = string.getWCharCount(str);
    local iconScale = scale or 1;

    for i = 1, len do
        local char = (string.getWChar(str, i));

        if char == text then
            -- 命中，处理之
            local letter = textNode:getVirtualRenderer():getLetter(i - 1);
            local posX = letter:getPositionX();
            local posY = letter:getPositionY();
            local anchorPoint = letter:getAnchorPoint();
            local icon;

            if type(iconPath) == "string" then
                icon = ccui.ImageView:create(iconPath);
            else
                icon = iconPath;
            end
            icon:setAnchorPoint(anchorPoint);
            icon:setPosition(posX, posY);
            icon:setScale(iconScale);
            letter:setVisible(false);
            textNode:addChild(icon);
        end
    end
end

------------------
-- 剩余时间描述
-- 一小时以上，显示 "Xh"
-- 一小时以内，显示 "mm:ss"
-- @param remainingTime     剩余时间
function remainingTimeDesc(remainingTime)
    if remainingTime > 3600 then
        return tostring(math.floor(remainingTime / 3600)) .. "h";
    else
        local minute = 0;
        local second = 0;
        if remainingTime >= 60 then
            minute = math.floor((remainingTime % 3600) / 60);
        end

        second = math.floor(remainingTime % 60);
        return string.format(getLocStr("remaining_time"), minute, second);
    end
end

------------------
-- 剩余时间描述（descType == disappear)
-- 一小时以上，显示 "(x小时后消失)"
-- 一小时以内，显示 "(x分钟后消失)"
-- 一分钟以内，显示 "(x秒后消失)"
-- 剩余时间描述（descType == leave)
-- 一小时以上，显示 "(x小时后离开)"
-- 一小时以内，显示 "(x分钟后离开)"
-- 一分钟以内，显示 "(x秒后离开)"
-- @param descType 描述类型
-- @param remainingTime     剩余时间
function remainingTimeDesc3(remainingTime, descType)
    local strKey = "";
    if remainingTime > 3600 then
        local hours = math.floor(remainingTime / 3600);
        strKey = "after_hours_" .. descType;

        return string.format(getLocStr(strKey), hours);
    elseif remainingTime >= 60 then
        local minute = 0;
        minute = math.floor((remainingTime % 3600) / 60);
        strKey = "after_minutes_" .. descType;
        return string.format(getLocStr(strKey), minute);
    else
        local second = 0;
        second = math.floor(remainingTime % 60);
        strKey = "after_seconds_" .. descType;
        return string.format(getLocStr(strKey), second);
    end
end

-----------------------------------------------
-- 获取起止时间字符串
-- @param startTime 开始时间
-- @param endTime 结束时间
function getTimeRangeStr(startTime, endTime)
    local function getDateDesc(t)
        t = t - TimeM.getTimeOffsetDifference();
        if getLang() == "zh" or getLang() == "tw" then
            local month = os.date("%m", t);
            local day = os.date("%d", t);
            return string.format(getLocStr("date_desc"), month, day);
        end

        return os.date("%x", t)
    end

    local function getTimeDesc(t)
        t = t - TimeM.getTimeOffsetDifference();
        local hour = os.date("%H", t);
        local min = os.date("%M", t);
        return hour..":"..min;
    end

    local dashStr = getLocStr("dash_symbol");

    local startDate = getDateDesc(startTime);
    local endDate = getDateDesc(endTime);

    local startTimeDesc = getTimeDesc(startTime);
    local endTimeDesc = getTimeDesc(endTime);

    if startDate == endDate then
        return spliceLocStr(startDate, " ", startTimeDesc, dashStr, endTimeDesc);
    else
        return spliceLocStr(startDate, " ", startTimeDesc, dashStr, endDate, " ", endTimeDesc);
    end
end

------------------
-- 剩余时间描述
-- 始终显示XX:XX:XX
-- @param remainingTime     剩余时间
function remainingTimeDesc2(remainingTime)
    if remainingTime <= 0 then
        return string.format(getLocStr("left_time"), 0, 0, 0)
    end

    local timeText;
    local hour = math.floor(remainingTime / 3600);
    local minute = math.floor((remainingTime - hour * 3600) / 60);
    local second = math.floor(remainingTime % 60);
    timeText = string.format(getLocStr("left_time"), hour, minute, second);
    return timeText;
end

------------------
-- 字体是否使用全角字符
function isFullWidthLang()
    local locVer = getLang();
    local fullWidthFont = { "zh", "tw", "ja", "ko", };

    if table.indexOf(fullWidthFont, locVer) ~= -1 then
        -- 使用全角字符
        return true;
    else
        return false;
    end
end

----------------------
-- 拆分字符
-- @param szFullString  待拆分字符串
-- @param szSeparator   拆分标示
function splitString(szFullString, szSeparator)
    local nFindStartIndex = 1;
    local nSplitIndex = 1;
    local nSplitArray = {};
    while true do
        local nFindLastIndex = string.find(szFullString, szSeparator, nFindStartIndex);
        if not nFindLastIndex then
            nSplitArray[nSplitIndex] = string.sub(szFullString, nFindStartIndex, string.len(szFullString));
            break;
        end
        nSplitArray[nSplitIndex] = string.sub(szFullString, nFindStartIndex, nFindLastIndex - 1);
        nFindStartIndex = nFindLastIndex + string.len(szSeparator);
        nSplitIndex = nSplitIndex + 1;
    end
    return nSplitArray;
end

-- 拆分对齐标签
-- @param seg 字符串
-- 返回{去除标签文本, 对齐方式}
function getMsgAlignmentTag(msg)
    if string.sub(msg, 1, 6) == "[left]" then
        return {string.sub(msg, 7), "left"};
    elseif string.sub(msg, 1, 7) == "[right]" then
        return {string.sub(msg, 8), "right"};
    else
        return {msg, "center"};
    end
end

-------------------------------
-- 设置商城货币文本的样式
-- @param priceLabel 价格文本
function setCurrencyTextStyle(priceLabel)
    -- 1.为兼容各种货币单位的显示，这里使用系统字
    -- 2.为解决系统字体变细的问题，统一加大字号再缩小label

--    priceLabel:setFontSize(TextStyleM.TEXT_SIZE_SMALL2 + 6);
--    priceLabel:setTextColor(TextStyleM.TEXT_COLOR_WHITE);
--    priceLabel:setScale(0.92);
--    local outlineColor = TextStyleM.TEXT_OUTLINE_COLOR_DARK;
--    priceLabel:enableOutline(outlineColor, 3);

    TextStyleM.setTextStyle(priceLabel, TextStyleM.TEXT_SIZE_SMALL, TextStyleM.TEXT_COLOR_ORANGE, true);
end

-------------------------------
-- 转换美元符号（只应用于华刚海报体！！）
-- @param priceLabel 价格文本
function changeDollerCharacter(str)
    local ret;
    if getLang() == "zh" then
       str = string.gsub(str, "%$", "\239\188\132");
    end
    return str;
end

-------------------------------
-- 逗号分割的数字类型
-- @param num 传入数字
function changeNumString(num)
    -- 逗号分段战力
    if type(num) ~= "number" then
        return "";
    end

    local retNum = "";
    while num >= 1000 do
        if retNum == "" then
            retNum = string.format("%03d", num % 1000);
        else
            retNum = string.format(getLocStr("thousand_type"), string.format("%03d", num % 1000), retNum);
        end

        num = math.floor(num / 1000);
    end

    if retNum == "" then
        retNum = tostring(num % 1000);
    else
        retNum = string.format(getLocStr("thousand_type"), tostring(num % 1000), retNum);
    end
    return retNum;
end

-------------------------------
-- 修改数字形式
-- @param num 传入数字
function changeNumType(num)
    local numStr = "";
    if num >= 1000000 then
        local pNum = num / 1000000;
        -- 先保留所有有小数，再截断
        pNum = string.format("%0.6f", pNum);
        local num = string.sub(tostring(pNum), 1,-5);
        num = deleteZero(num);
        numStr = num .. "M";
    elseif num >= 1000 then
        local pNum = num / 1000;
        pNum = string.format("%0.3f", pNum);
        local num = string.sub(tostring(pNum), 1,-2);
        num = deleteZero(num);

        numStr = num .. "K";
    else
        numStr = tostring(num);
    end
    return numStr;
end

-- 末尾去0
function deleteZero(numStr)
    -- numStr（11.33）小数点后两位
    -- 倒数第一个数
    local num1 = tonumber(string.sub(numStr, -1, -1));
    -- 倒数第二个数
    local num2 = tonumber(string.sub(numStr, -2, -2));

    if num1 > 0 then
        return numStr;
    end

    numStr = string.sub(numStr, 1, -2);

    if num2 > 0 then
        return numStr;
    end

    numStr = string.sub(numStr, 1, -3);
    return numStr;
end

-- 去除首尾的空格和tab
function removeTabAndSpace(str)
    return string.match(str, "^[　%s]*(.-)[　%s]*$");
end